package cn.initcap.shiro;

import cn.initcap.entity.Permission;
import cn.initcap.entity.Role;
import cn.initcap.entity.User;
import cn.initcap.service.UserService;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

public class AuthRealm extends AuthorizingRealm {

    /**
     * 用户登录次数计数  redisKey 前缀
     */
    private final String SHIRO_LOGIN_COUNT = "shiro_login_count_";
    /**
     * 用户登录是否被锁定    一小时 redisKey 前缀
     */
    private final String SHIRO_IS_LOCK = "shiro_is_lock_";
    private final String LOCK = "LOCK";
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserService userService;

    /**
     * 认证登录
     *
     * @param token 信息
     * @return 认证
     * @throws AuthenticationException 认证异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        User user = userService.findByUsername(username);

        Md5Hash md5Hash = new Md5Hash(usernamePasswordToken.getPassword(), "initcap");

        //访问一次，计数一次
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        //记录密码输入错误此时
        if (!md5Hash.toString().equals(user.getPassword())) {
            opsForValue.increment(SHIRO_LOGIN_COUNT + username, 1);
            //计数大于5时，设置用户被锁定一小时
            if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + username)) >= 5) {
                opsForValue.set(SHIRO_IS_LOCK + username, LOCK);
                stringRedisTemplate.expire(SHIRO_IS_LOCK + username, 1, TimeUnit.HOURS);
            }
            if (LOCK.equals(opsForValue.get(SHIRO_IS_LOCK + username))) {
                throw new DisabledAccountException("由于密码输入错误次数大于5次，帐号已经禁止登录！");
            }
        }


        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
        //设置加密的盐值
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("initcap"));
        return authenticationInfo;
    }

    /**
     * 授权
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
        List<String> permissionList = new ArrayList<>();
        List<String> roleNameList = new ArrayList<>();
        Set<Role> roleSet = user.getRoles();
        if (CollectionUtils.isNotEmpty(roleSet)) {
            for (Role role : roleSet) {
                roleNameList.add(role.getRname());
                Set<Permission> permissionSet = role.getPermissions();
                if (CollectionUtils.isNotEmpty(permissionSet)) {
                    for (Permission permission : permissionSet) {
                        permissionList.add(permission.getName());
                    }
                }
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permissionList);
        info.addRoles(roleNameList);
        return info;
    }
}
